<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
    <meta name="description" content="For debugging voxel picking." />
    <meta name="cesium-sandcastle-labels" content="Showcases, 3D Tiles" />
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script type="module" src="../load-cesium-es6.js"></script>
  </head>
  <body
    class="sandcastle-loading"
    data-sandcastle-bucket="bucket-requirejs.html"
  >
    <style>
      @import url(../templates/bucket.css);
      #pickReport {
        background-color: rgba(0, 0, 0, 0.5);
      }
      #pickedCoordinate {
        display: inline-block;
        padding: 1ch 1ch 1ch 3ch;
      }
      #pickedColor {
        display: inline-block;
        width: 1em;
        height: 1em;
        background-color: white;
        margin-left: 1ch;
      }
    </style>
    <div id="cesiumContainer" class="fullSize"></div>
    <div id="loadingOverlay"><h1>Loading...</h1></div>
    <div id="toolbar">
      <div id="pickReport">
        <div id="pickedCoordinate">Sample center</div>
        <div id="pickedColor"></div>
      </div>
    </div>
    <script id="cesium_sandcastle_script">
      window.startup = async function (Cesium) {
        "use strict";
        //Sandcastle_Begin
        const viewer = new Cesium.Viewer("cesiumContainer", {
          baseLayer: Cesium.ImageryLayer.fromProviderAsync(
            Cesium.TileMapServiceImageryProvider.fromUrl(
              Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII")
            )
          ),
          baseLayerPicker: false,
          geocoder: false,
          timeline: false,
          animation: false,
        });

        const { scene, camera } = viewer;

        scene.debugShowFramesPerSecond = true;

        const scratchColor = new Cesium.Color();

        function ProceduralMultiTileVoxelProvider(shape) {
          this.shape = shape;
          this.dimensions = new Cesium.Cartesian3(4, 4, 4);
          this.names = ["color"];
          this.types = [Cesium.MetadataType.VEC4];
          this.componentTypes = [Cesium.MetadataComponentType.FLOAT32];
          this._levelCount = 3;
        }

        ProceduralMultiTileVoxelProvider.prototype.requestData = function (
          options
        ) {
          const { tileLevel, tileX, tileY, tileZ } = options;

          if (tileLevel >= this._levelCount) {
            return undefined;
          }

          const dimensions = this.dimensions;
          const type = this.types[0];
          const randomSeed =
            tileZ * dimensions.y * dimensions.x + tileY * dimensions.x + tileX;
          const dataTile = constructRandomTileData(
            dimensions,
            type,
            randomSeed
          );

          return Promise.resolve([dataTile]);
        };

        function constructRandomTileData(dimensions, type, randomSeed) {
          Cesium.Math.setRandomNumberSeed(randomSeed);
          const voxelCount = dimensions.x * dimensions.y * dimensions.z;
          const channelCount = Cesium.MetadataType.getComponentCount(type);
          const dataColor = new Float32Array(voxelCount * channelCount);

          for (let z = 0; z < dimensions.z; z++) {
            const indexZ = z * dimensions.y * dimensions.x;
            for (let y = 0; y < dimensions.y; y++) {
              const indexZY = indexZ + y * dimensions.x;
              for (let x = 0; x < dimensions.x; x++) {
                const lerperX = x / (dimensions.x - 1);
                const lerperY = y / (dimensions.y - 1);
                const lerperZ = z / (dimensions.z - 1);

                const h = Cesium.Math.nextRandomNumber();
                const s = 1.0 - lerperY * 0.2;
                const l = 0.5;
                const color = Cesium.Color.fromHsl(h, s, l, 1.0, scratchColor);

                const random2 = Cesium.Math.nextRandomNumber();
                const alphaRandom = Math.floor(random2 + 0.5);

                const index = (indexZY + x) * channelCount;
                dataColor[index + 0] = color.red;
                dataColor[index + 1] = color.green;
                dataColor[index + 2] = color.blue;
                dataColor[index + 3] = alphaRandom;
              }
            }
          }

          return dataColor;
        }

        const provider = new ProceduralMultiTileVoxelProvider(
          Cesium.VoxelShapeType.BOX
        );

        const customShader = new Cesium.CustomShader({
          fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
    {
      vec3 voxelNormal = normalize(czm_normal * fsInput.voxel.surfaceNormal);
      float diffuse = max(0.0, dot(voxelNormal, czm_lightDirectionEC));
      float lighting = 0.5 + 0.5 * diffuse;

      int tileIndex = fsInput.voxel.tileIndex;
      int sampleIndex = fsInput.voxel.sampleIndex;
      vec3 cellColor = fsInput.metadata.color.rgb * lighting;
      if (tileIndex == u_selectedTile && sampleIndex == u_selectedSample) {
        material.diffuse = mix(cellColor, vec3(1.0), 0.5);
        material.alpha = fsInput.metadata.color.a;
      } else {
        material.diffuse = cellColor;
        material.alpha = fsInput.metadata.color.a;
      }
    }`,
          uniforms: {
            u_selectedTile: {
              type: Cesium.UniformType.INT,
              value: -1.0,
            },
            u_selectedSample: {
              type: Cesium.UniformType.INT,
              value: -1.0,
            },
          },
        });

        const modelMatrix = Cesium.Matrix4.fromScale(
          Cesium.Cartesian3.fromElements(
            Cesium.Ellipsoid.WGS84.maximumRadius,
            Cesium.Ellipsoid.WGS84.maximumRadius,
            Cesium.Ellipsoid.WGS84.maximumRadius
          )
        );

        const voxelPrimitive = scene.primitives.add(
          new Cesium.VoxelPrimitive({
            provider: provider,
            customShader: customShader,
            modelMatrix: modelMatrix,
          })
        );

        voxelPrimitive.nearestSampling = true;

        camera.flyToBoundingSphere(voxelPrimitive.boundingSphere, {
          duration: 0.0,
        });

        const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
        const pickedCoordinate = document.getElementById("pickedCoordinate");
        const pickedColor = document.getElementById("pickedColor");
        handler.setInputAction(function (movement) {
          const mousePosition = movement.endPosition;
          const voxelCell = scene.pickVoxel(mousePosition);
          if (!Cesium.defined(voxelCell)) {
            return;
          }
          const { tileIndex, sampleIndex, orientedBoundingBox } = voxelCell;
          const [x, y, z] = Object.values(orientedBoundingBox.center).map(
            Math.round
          );
          pickedCoordinate.innerHTML = `Sample center x = ${x}, y = ${y}, z = ${z}`;
          const rgbaValues = voxelCell.getProperty("color");
          const color = new Cesium.Color(...rgbaValues);
          pickedColor.style.backgroundColor = color.toCssColorString();

          const { customShader } = voxelCell.primitive;
          customShader.setUniform("u_selectedTile", tileIndex);
          customShader.setUniform("u_selectedSample", sampleIndex);
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        //Sandcastle_End
        Sandcastle.finishedLoading();
      };
      if (typeof Cesium !== "undefined") {
        window.startupCalled = true;
        window.startup(Cesium).catch((error) => {
          "use strict";
          console.error(error);
        });
      }
    </script>
  </body>
</html>
